#include "battery_monitor.h"
#include "bsp_adc.h"
#include "math.h"
#include "kalman.h"
#include "adc.h"

// 电池电压相关常量
#define R1 100000.0f    // 电阻R1 (100kΩ)
#define R2 100000.0f   // 电阻R2 (100kΩ)
#define DIODE_DROP 0.0f // 二极管压降

// #define VREFINT_CAL_ADDR ((uint16_t*)0x1FF80078)  // STM32 内部参考电压校准值的地址
#define ADC_MAX 4095.0f  // 12 位 ADC 最大值

// 声明外部的卡尔曼滤波器实例
extern KalmanFilter bat_kf;


// 使用方法
// BatteryData_t batteryData;
//
// batteryData.voltage = BatteryMonitor_ReadVoltage();
// batteryData.percentage = BatteryMonitor_VoltageToPercent(batteryData.voltage);


// 电池监测初始化函数
void BatteryMonitor_Init(void)
{
    BSP_ADC_Init();  // 初始化ADC硬件
    // 初始化卡尔曼滤波器（假设初始电压为3.7V）
    Kalman_Init(&bat_kf, 0.01f, 0.1f, 3.7f);
}

/**
 * @brief  获取电池电压
 * @author mdqr
 * @details 从 ADC 通道（PA1 -> ADC_CHANNEL_6）读取原始值，通过分压电路和卡尔曼滤波计算实际电池电压。
 * @return  float: 电池电压（单位：V）
 */
float BatteryMonitor_ReadVoltage(void)
{
    // 获取 ADC 的原始值（PA1 -> ADC_CHANNEL_6）
    uint16_t adc_raw_value = BSP_ADC_ReadRaw();

    // 获取参考电压
    float vdd = Read_VREF();  // 获取内部参考电压

    // 将 ADC 原始值转换为电压
    float adc_voltage = (adc_raw_value * vdd) / ADC_MAX;

    // 计算实际电池电压（通过分压电路计算）
    float battery_voltage = adc_voltage * ((R1 + R2) / R2) + DIODE_DROP;

    // 使用卡尔曼滤波器对电池电压进行平滑处理
    return Kalman_Update(&bat_kf, battery_voltage);
}

/**
 * @brief  将电池电压转换为电量百分比
 * @author mdqr
 * @details 根据预定义的电压-百分比放电曲线，使用分段线性插值法将电压映射为电量百分比。
 * @param  voltage: 电池电压（单位：V）
 * @return uint8_t: 电池电量百分比（0-100%）
 */
uint8_t BatteryMonitor_VoltageToPercent(float voltage)
{
    // 根据电池电压的放电曲线定义几个电压值与对应百分比
    const float points[] = {
        4.20f, 100.0f,
        4.06f, 90.0f,
        3.98f, 80.0f,
        3.92f, 70.0f,
        3.87f, 60.0f,
        3.82f, 50.0f,
        3.79f, 40.0f,
        3.77f, 30.0f,
        3.74f, 20.0f,
        3.68f, 10.0f,
        3.45f, 5.0f,
        3.00f, 0.0f
    };

    // 使用分段插值法将电压值映射为百分比
    for (int i = 0; i < 11; i++) {
        if (voltage >= points[i * 2 + 2]) {
            float v_low = points[i * 2 + 2];
            float p_low = points[i * 2 + 3];
            float v_high = points[i * 2];
            float p_high = points[i * 2 + 1];

            return (uint8_t)(p_low + (voltage - v_low) * (p_high - p_low) / (v_high - v_low));
        }
    }
    return 0;
}

/**
 * @brief  读取芯片内部参考电压（VREFINT）
 * @author mdqr
 * @details 通过 ADC 读取 VREFINT 通道值，并根据校准值计算实际 VDD 电压。
 * @return  float: 内部参考电压 VDD（单位：V）
 */
float Read_VREF(void)
{
    // 获取 VREFINT 校准值
    uint16_t vrefint_cal = *VREFINT_CAL_ADDR;

    // 读取 VREFINT 的 ADC 值
    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Channel = ADC_CHANNEL_VREFINT;
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_47CYCLES_5;

    HAL_ADC_ConfigChannel(&hadc1, &sConfig);
    HAL_ADC_Start(&hadc1);
    HAL_ADC_PollForConversion(&hadc1, 10);
    uint16_t vrefint_raw = HAL_ADC_GetValue(&hadc1);
    HAL_ADC_Stop(&hadc1);

    // 计算 VDD，公式： VDD = (VREFINT_CAL * 3.3V) / VREFINT_RAW
    float vdd = (float)vrefint_cal * 3.3f / vrefint_raw;
    return vdd;
}





